/*
 * Copyright (C) 2011 bus.fm
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.busfm.util;

import java.util.ArrayList;
import java.util.List;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;

/**
 * <p>
 * Title:ImageUtil
 * </p>
 * <p>
 * Description: ImageUtil
 * </p>
 * <p>
 * Copyright (c) 2011 www.bus.fm Inc. All rights reserved.
 * </p>
 * <p>
 * Company: bus.fm
 * </p>
 * 
 * 
 * @author jingguo0@gmail.com
 * 
 */

public class ImageUtil {

    private ImageUtil() {

    }

    public static Bitmap loadScalledBitmap(String path, int targetWidth, int targetHeight) {
        Bitmap bitmap = null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        options.inSampleSize = ImageUtil.computeSampleSize(options, -1, targetWidth * targetHeight);
        options.inJustDecodeBounds = false;

        try {

            bitmap = BitmapFactory.decodeFile(path, options);
        } catch (OutOfMemoryError e) {
            LogUtil.e(Constants.TAG, e.getMessage());
        }

        return bitmap;
    }

    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) {
        Bitmap roundedCornerBitmap = null;

        try {
            roundedCornerBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
        } catch (OutOfMemoryError e) {
            LogUtil.e(Constants.TAG, e.getMessage());
        }

        if (null != roundedCornerBitmap) {
            Canvas canvas = new Canvas(roundedCornerBitmap);
            final int color = 0xff424242;
            final Paint paint = new Paint();
            final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
            final RectF rectF = new RectF(rect);

            paint.setAntiAlias(true);
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(color);
            canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
            canvas.drawBitmap(bitmap, rect, rect, paint);
        }

        recycle(bitmap);
        return roundedCornerBitmap;
    }

    public static Bitmap createReflectionImageWithOrigin(Bitmap originalImage) {
        //		ArrayList<Bitmap> result = new ArrayList<Bitmap>();
        // The gap we want between the reflection and the original image.
        final int reflectionGap = 0;

        // This will not scale but will flip on the Y axis.
        Matrix matrix = new Matrix();
        matrix.preScale(1, -1);
        matrix.postScale(1, 0.5f);

        Bitmap reflectionImage = null;
        Bitmap bitmapWithReflection = null;

        int width = originalImage.getWidth();
        int height = originalImage.getHeight();

        /*
         * Create a Bitmap with the flip matrix applied to it. We only want
         * the bottom half of the image.
         */

        try {
            reflectionImage = Bitmap.createBitmap(originalImage, 0, 0, width, height, matrix, false);
        } catch (OutOfMemoryError e) {
            LogUtil.e(Constants.TAG, e.getMessage());
        }

        /*
         * Create a new bitmap with same width but taller to fit reflection.
         */
        try {
            bitmapWithReflection = Bitmap.createBitmap(width, (height + height / 4 + reflectionGap), Config.ARGB_8888);
        } catch (OutOfMemoryError e) {
            LogUtil.e(Constants.TAG, e.getMessage());
        }

        /*
         * Create a new Canvas with the bitmap that's big enough for the
         * image plus gap plus reflection.
         */
        if (null != bitmapWithReflection) {
            Canvas canvas = new Canvas(bitmapWithReflection);
            canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
            // Draw in the original image.
            canvas.drawBitmap(originalImage, 0, 0, null);

            // Draw in the gap
            Paint deafaultPaint = new Paint();
            canvas.drawRect(0, height, width, height + reflectionGap, deafaultPaint);

            // Draw in the reflection
            if (null != reflectionImage) {
                canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
            }

            /*
             * Create a shader that is a linear gradient that covers the
             * reflection.
             */
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            LinearGradient shader = new LinearGradient(
                0, originalImage.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff,
                0x00ffffff, TileMode.CLAMP);
            // Set the paint to use this shader (linear gradient)
            paint.setShader(shader);
            // Set the Transfer mode to be porter duff and destination in
            paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
            // Draw a rectangle using the paint with our linear gradient
            canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint);

            recycle(originalImage);
            recycle(reflectionImage);
        }

        return bitmapWithReflection;
    }

    private static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);

        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }

        return roundedSize;
    }

    private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;

        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
            Math.floor(w / minSideLength), Math.floor(h / minSideLength));

        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }

        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
            return 1;
        } else if (minSideLength == -1) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }

    private static void recycle(Bitmap bitmap) {
        if ((null != bitmap) && !bitmap.isRecycled()) {
            bitmap.recycle();
            bitmap = null;
        }
    }

}
